20

Trying to import a code from python2 to python 3 and this problem happens

    <ipython-input-53-e9f33b00348a> in aesEncrypt(text, secKey)
     43 def aesEncrypt(text, secKey):
     44     pad = 16 - len(text) % 16
---> 45     text = text.encode("utf-8") + (pad * chr(pad)).encode("utf-8")
     46     encryptor = AES.new(secKey, 2, '0102030405060708')
     47     ciphertext = encryptor.encrypt(text)

AttributeError:'bytes' object has no attribute 'encode'

If I remove .encode("utf-8") the error is "can't concat str to bytes". Apparently pad*chr(pad) seems to be a byte string. It cannot use encode()

    <ipython-input-65-9e84e1f3dd26> in aesEncrypt(text, secKey)
     43 def aesEncrypt(text, secKey):
     44     pad = 16 - len(text) % 16
---> 45     text = text.encode("utf-8") + (pad * chr(pad))
     46     encryptor = AES.new(secKey, 2, '0102030405060708')
     47     ciphertext = encryptor.encrypt(text)

TypeError: can't concat str to bytes

However, the weird thing is that if i just try the part along. encode() works fine.

text = { 'username': '', 'password': '', 'rememberLogin': 'true' }
text=json.dumps(text)
print(text)
pad = 16 - len(text) % 16 
print(type(text))
text = text + pad * chr(pad) 
print(type(pad * chr(pad)))
print(type(text))
text = text.encode("utf-8") + (pad * chr(pad)).encode("utf-8") 
print(type(text))

{"username": "", "password": "", "rememberLogin": "true"}
<class 'str'>
<class 'str'>
<class 'str'>
<class 'bytes'>
11
  • chr returns a string that gets multiplied and encoded as bytes. The problem here is that text is bytes. How are you calling aesEncrypt? You need to provide a minimal reproducible example. Commented Feb 24, 2020 at 2:57
  • Python2 strings are implicitly bytes objects while Python3 strings are unicode. So this makes sense. This looks like the right way to do it to me - the way you added .encode('utf-8'). But this won't be backward compatible with Python2 - is that what the issue is? @RoyDai Commented Feb 24, 2020 at 3:04
  • Maybe a portable way to control the codec is to import codec and using it's encode() and decode() module functions. Commented Feb 24, 2020 at 3:07
  • pad = 16 - len("dummy") % 16; (pad * chr(pad)).encode('utf-8') b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b' Works fine on Python3.. unless there were pad bytes it couldn't encode. Commented Feb 24, 2020 at 3:18
  • @Todd Thanks very much for the reply. I tried .encode('utf-8') works for isolated sessions but it didnt work for first case i dont now why. Commented Feb 24, 2020 at 7:16

3 Answers 3

8

If you don't know if a stringlike object is a Python 2 string (bytes) or Python 3 string (unicode). You could have a generic converter.

Python3 shell:

>>> def to_bytes(s):
...     if type(s) is bytes:
...         return s
...     elif type(s) is str or (sys.version_info[0] < 3 and type(s) is unicode):
...         return codecs.encode(s, 'utf-8')
...     else:
...         raise TypeError("Expected bytes or string, but got %s." % type(s))
...         
>>> to_bytes("hello")
b'hello'
>>> to_bytes("hello".encode('utf-8'))
b'hello'

On Python 2 both these expressions evaluate to True: type("hello") == bytes and type("hello") == str. And type(u"hello") == str evaluates to False, while type(u"hello") == unicode is True.

On Python 3 type("hello") == bytes is False, and type("hello") == str is True. And type("hello") == unicode raises a NameError exception since unicode isn't defined on 3.

Python 2 shell:

>>> to_bytes(u"hello")
'hello'
>>> to_bytes("hello")
'hello'
Sign up to request clarification or add additional context in comments.

Comments

2

Thanks to @Todd, he solved issue. (pad * chr(pad))is bytes while problems lies with aesEncrypt(text, secKey). It has been called twice with text as str for the first time while as bytes for the second time.

The solution is to make sure that the input text is of str type.

Comments

0

Since the first parameter of AES.new is bytes/bytearray/memoryview, and I assume that text is already of type bytes, then we just have to convert the pad part from unicode to bytes.

text = text + (pad * chr(pad)).encode("utf-8")

To be extra safe, you may encode text conditionally before concatenating with pad.

if not isinstance(text, bytes):
    text = text.encode('utf-8')

2 Comments

@Todd how about checking if it is not bytes? Kind of lazy, but would work given the input is either bytes or unicode
you caught me as I just finished up dissecting this difference - testing if it's not bytes would leave the possibility it could be None or another type that isn't a str or unicode object. See if you can find the answer in my notes on my answer post. let me know if i accounted for what case you have in mind.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.