跳过elif声明?

时间:2017-05-22 22:40:48

标签: python-3.x pycrypto cbc-mode pycryptodome

我尝试使用pycryptodome创建一个简单的加密/解密,但不断收到以下错误:

ValueError: Error 3 while encrypting in CBC mode
经过一些挖掘后,我发现如果没有足够的数据加密,你会收到这个错误,因为没有填充有效。问题是我添加了填充功能。调试后,似乎我的代码字面上完全跳过填充部分并导致此错误。我做错了什么?

import os, random
from Crypto.Cipher import AES
from Crypto.Hash import SHA256

def encrypt(key, filename):
    chunksize = 64*1024
    outputfile = filename + "(encrypted)"
    filesize = str(os.path.getsize(filename)).zfill(16)

    IV =''
    for i in range(16):
        IV += chr(random.randint(0, 0xFF))

    encryptor = AES.new(key, AES.MODE_CBC, IV.encode("latin-1"))

    with open(filename, 'rb') as infile:
        with open(outputfile, 'wb') as outfile:
            outfile.write(filesize.encode("latin-1"))
            outfile.write(IV.encode("latin-1"))

            while True:
                chunk = infile.read(chunksize)
                print(len(chunk))
                if len(chunk) == 0:
                    break
                elif len(chunk) % 16 != 0:
                    chunk += ' ' * (16 - (len(chunk) % 16))

                outfile.write(encryptor.encrypt(chunk))

def decrypt(key, filename):
    chunksize = 64 *1024
    outputfile = filename[:11]
    with open(filename, 'rb') as infile:
        filesize = int(infile.read(16))
        IV = infile.read(16)
        decryptor = AES.new(key, AES.MODE_CBC, IV.encode("latin-1"))
        with open(outputfile, 'wb') as outfile:
            while True:
                chunk = infile.read(chunksize)
                if len(chunk) == 0:
                    break
                outfile.write(decryptor.decrypt(chunk))
            outfile.truncate(filesize)

def getkey (password):
    hasher = SHA256.new(password.encode("latin-1"))
    return hasher.digest()

def main():
    choice = input ("do you want to [E]ncrypt of [D]ecrypt?")
    if choice == 'E':
        filename = input("File to encrypt >")
        password = input("Password >")
        encrypt(getkey(password), filename)
        print("Encryption done!")
    elif choice == 'D':
        filename = input("File to Decrypt >")
        password = input("Password >")
        decrypt(getkey(password), filename)
        print("Decryption done!")
    else:
        print("No option selected")

if __name__ == '__main__':
    main()

*我正在使用python 3.6

编辑: 以下是运行代码时的完整控制台输出:

   C:\Users\itayg\AppData\Local\Programs\Python\Python36\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.2\helpers\pydev\pydevd.py" --multiproc --qt-support --client 127.0.0.1 --port 21111 --file C:/Users/itayg/PycharmProjects/PyCrypto/encrypt.py
Connected to pydev debugger (build 171.4249.47)
pydev debugger: process 12876 is connecting

do you want to [E]ncrypt of [D]ecrypt?E
File to encrypt >grades.jpg
Password >123
65536
49373
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.2\helpers\pydev\pydevd.py", line 1585, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.2\helpers\pydev\pydevd.py", line 1015, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.2\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "C:/Users/itayg/PycharmProjects/PyCrypto/encrypt.py", line 66, in <module>
    main()
  File "C:/Users/itayg/PycharmProjects/PyCrypto/encrypt.py", line 55, in main
    encrypt(getkey(password), filename)
  File "C:/Users/itayg/PycharmProjects/PyCrypto/encrypt.py", line 29, in encrypt
    outfile.write(encryptor.encrypt(chunk))
  File "C:\Users\itayg\AppData\Local\Programs\Python\Python36\lib\site-packages\pycryptodome-3.4.6-py3.6-win-amd64.egg\Crypto\Cipher\_mode_cbc.py", line 167, in encrypt
    raise ValueError("Error %d while encrypting in CBC mode" % result)
ValueError: Error 3 while encrypting in CBC mode

1 个答案:

答案 0 :(得分:2)

好的,让我们解决一些代码错误的问题。首先是最明显的一个 - 你的填充会破坏Python 3.5+(并且你的用户&#39;菜单会在2.x上中断),因为infile.read()会给你bytes数组这样的尝试添加chunk += ' ' * (16 - (len(chunk) % 16))形成的字符串会导致错误。您需要先将空白区域转换为bytes数组:chunk += b' ' * (16 - (len(chunk) % 16))

但像这样的空白填充是一个坏主意 - 当你以后解密你的文件时,你怎么知道你添加了多少(如果有的话)填充?你需要将它存储在某个地方 - 你可以在标题中找到它。通过filesize值告诉潜在的攻击者你的文件究竟有多大以及添加了多少填充,这使你能够进行填充oracle攻击(这可能与下面的代码有关,因此不要使用它来传递消息而不添加适当的MAC)。

你可以使用大量强大的填充方案 - 我个人更喜欢PKCS#7,它只是填充不均匀的块或添加一个n个字节数的全新块,其值为{ {1}} - 这样,在解密之后,您可以从块中选择最后一个字节并确切地知道填充了多少字节,这样您就可以剥离它们。因此,请将您的加密部分替换为:

n

我还更改了您的def encrypt(key, filename): outputfile = filename + "(encrypted)" chunksize = 1024 * AES.block_size # use the cipher's defined block size as a multiplier IV = bytes([random.randint(0, 0xFF) for _ in range(AES.block_size)]) # bytes immediately encryptor = AES.new(key, AES.MODE_CBC, IV) with open(filename, 'rb') as infile: with open(outputfile, 'wb') as outfile: outfile.write(IV) # write the IV padded = False while not padded: # loop until the last block is padded chunk = infile.read(chunksize) chunk_len = len(chunk) # if no more data or the data is shorter than the required block size if chunk_len == 0 or chunk_len % AES.block_size != 0: padding = AES.block_size - (chunk_len % AES.block_size) chunk += bytes([padding]) * padding # on Python 2.x replace with: chunk += chr(padding_len) * padding_len padded = True outfile.write(encryptor.encrypt(chunk)) 以匹配您正在使用的块大小(chunksize的倍数) - 它恰好是64是16的倍数但您应该注意那些事情。

现在我们已经对加密进行了解决,解密就是这一切,但是反过来 - 解密所有块,读取最后一个块的最后一个字节,并从后面删除AES.block_size个字节数,以匹配最后一个字节:

n