Search code examples
pythonencryptionpadding

Trouble with encode + encrypt + pad using same code for python2 and python3


Disclaimer: I understand that the following is not suited to give "security" in a production environment. It is simply meant as "a little bit better" than using XOR or rot13 on sensitive data that is stored on my system.

I put together the following code to allow me to use AES encryption for those sensitive values. AES requires 16 byte chunks; so I need padding. And I want to save that data in text files; so I added base64 encoding:

from __future__ import print_function
from Crypto.Cipher import AES
import base64

crypto = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s: s[0:-ord(s[-1])]


def scramble(data):
    return base64.b64encode(crypto.encrypt(pad(data)))


def unscramble(data):
    return unpad(crypto.decrypt(base64.b64decode(data)))

incoming = "abc"
print("in: {}".format(incoming))
scrambled = scramble(incoming)
print("scrambled: {}".format(scrambled))
andback= unscramble(scrambled)
print("reversed : {}".format(andback))

For python2; that prints:

in: abc

scrambled: asEkqlUDiqlUpW1lw09UlQ==

reversed :

For python3; I run into

unpad = lambda s: s[0:-ord(s[-1])]
TypeError: ord() expected string of length 1, but int found

Two questions:

  • What is wrong with my "reverse" path with python2, why doesn't it print "abc"?
  • I understand that error message using python3; but I am wondering: what is the correct, canonical way to solve this problem in a way that works for both python2 and python3?

Solution

  • One problem with your code is that you are using the same cipher object for both encryption and decryption. This won't work, as the cipher objects are stateful:PyCrypto Documentation

    You can create another object for decrypting, as in: crypto2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456'), and then use this object to decrypt.