Roy Dai
Roy Dai

Reputation: 553

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

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'>

Upvotes: 20

Views: 116009

Answers (3)

Roy Dai
Roy Dai

Reputation: 553

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.

Upvotes: 2

Todd
Todd

Reputation: 5395

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'

Upvotes: 8

Carson Ip
Carson Ip

Reputation: 1946

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')

Upvotes: 0

Related Questions