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

前端 未结 4 2069
渐次进展
渐次进展 2021-01-19 04:07

Disclaimer: I understand that the following is not suited to give "security" in a production environment. It is simply meant as "a little bit

相关标签:
4条回答
  • 2021-01-19 04:19

    More of an addendum: as result of the answers I got; and digging into AES encryption a bit deeper I figured that the Cipher AES API actually allows for "unpadded" input. I rewrote my code to:

    from __future__ import print_function
    from Crypto.Cipher import AES
    from Crypto.Util import Counter
    
    from base64 import b64decode, b64encode
    
    def scramble(data):
        crypto = AES.new('This is a key123', AES.MODE_CTR, 'This is an IV456', counter=Counter.new(128))
        return b64encode(crypto.encrypt(data))
    
    
    def unscramble(data):
        crypto = AES.new('This is a key123', AES.MODE_CTR, 'This is an IV456', counter=Counter.new(128))
        return crypto.decrypt(b64decode(data))
    
    
    incoming = "123456801DEF"
    print("in: {}".format(incoming))
    scrambled = scramble(incoming)
    print("scrambled: {}".format(scrambled))
    andback = unscramble(scrambled)
    print("reversed : {}".format(andback))
    

    And now I receive the expected results!

    The trick is that I can't reuse the AES object; so a new one needs to be created; and in addition to that AES also offers the CTR mode - and that one does padding internally!

    0 讨论(0)
  • 2021-01-19 04:21

    One problem is that the Crypto module returns byte strings in Python3.

    So when you use s[-1], you actually get an integer and no longer a byte string. The portable way is to use s[-1:] which correctly gives a character in Python2 and a byte string suitable for ord in Python3:

    unpad = lambda s: s[0:-ord(s[-1:])]
    
    0 讨论(0)
  • 2021-01-19 04:35

    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.

    0 讨论(0)
  • 2021-01-19 04:37

    Generally, code that handles binary data properly in both Python 2 and Python 3 can get a little messy. As you discovered, when you iterate over a bytes string in Python 3 you get integers, not characters.

    Thus in Python 2, this code

    print([i for i in b'ABCDE'])
    print([ord(c) for c in 'ABCDE'])
    

    outputs

    ['A', 'B', 'C', 'D', 'E']
    [65, 66, 67, 68, 69]
    

    whereas in Python 3 it outputs

    [65, 66, 67, 68, 69]
    [65, 66, 67, 68, 69]
    

    The clean way to handle this is to simply write separate code for the two versions. But it is possible to write code that works on both versions.

    Here's a modified version of the code you posted in the question. It also handles the statefulness of AES by creating a new AES cipher object each time you encrypt or decrypt.

    from __future__ import print_function
    from Crypto.Cipher import AES
    import base64
    
    BS = 16
    
    def pad(s):
        padsize = BS - len(s) % BS
        return (s + padsize * chr(padsize)).encode('utf-8')
    
    def unpad(s):
        s = s.decode('utf-8')
        offset = ord(s[-1])
        return s[:-offset]
    
    def scramble(data, key, iv):
        crypto = AES.new(key, AES.MODE_CBC, iv)
        raw = crypto.encrypt(pad(data))
        return base64.b64encode(raw)
    
    def unscramble(data, key, iv):
        crypto = AES.new(key, AES.MODE_CBC, iv)
        raw = crypto.decrypt(base64.b64decode(data))
        return unpad(raw)
    
    key = b'This is a key123'
    iv = b'This is an IV456'
    
    incoming = "abc def ghi jkl mno"
    print("in: {0!r}".format(incoming))
    
    scrambled1 = scramble(incoming, key, iv)
    print("scrambled: {0!r}".format(scrambled1))
    
    incoming = "pqr stu vwx yz0 123"
    print("in: {0!r}".format(incoming))
    
    scrambled2 = scramble(incoming, key, iv)
    print("scrambled: {0!r}".format(scrambled2))
    
    andback = unscramble(scrambled2, key, iv)
    print("reversed : {0!r}".format(andback))
    
    andback = unscramble(scrambled1, key, iv)
    print("reversed : {0!r}".format(andback))
    

    Python 3 output

    in: 'abc def ghi jkl mno'
    scrambled: b'C2jA5/WngDo55J7TG3uiArEO7hhyTPld/A3v52t+ANc='
    in: 'pqr stu vwx yz0 123'
    scrambled: b'FsFAKA2SbhCTimURy0W8+tM4iqLhNlK3OZrRuuYpMpY='
    reversed : 'pqr stu vwx yz0 123'
    reversed : 'abc def ghi jkl mno'
    

    In Python 2, the reversed output looks like

    reversed : u'pqr stu vwx yz0 123'
    reversed : u'abc def ghi jkl mno'
    

    because we're decoding the bytes to Unicode.


    I turned the pad and unpad functions into proper def functions. That makes them a little easier to read. Also, it's generally considered bad style to use lambda for named functions: lambda is supposed to be used for anonymous functions.

    0 讨论(0)
提交回复
热议问题